package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.apis.article.IArticleClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.builder.ArticleDtoBuilder;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    private WmNewsMapper newsMapper;

    @Autowired
    private GreenTextScan greenTextScan;

    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private IArticleClient articleClient;

    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private WmChannelMapper channelMapper;

    @Autowired
    private WmSensitiveMapper sensitiveMapper;

    @Autowired
    private Tess4jClient tess4jClient;

    /**
     * 自媒体文章审核
     *
     * @param id 自媒体文章id
     */
    @Override
    @Async
    @GlobalTransactional
    public void autoScanWmNews(Integer id) throws Exception {

        //1. 更加文章的id查询文章
        WmNews wmNews = newsMapper.selectById(id);

        //2. 如果没有查询到文章则抛出异常
        if(null == wmNews){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //文中进行审核的时候只有状态为1才需要进行审核 其他的状态都不需要审核
        if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
            //3. 解析文章的内容数据-所有的文本内容和所有的图片内容
            Map<String, Object> data = handleTextAndImages(wmNews);


            //审核自定义敏感词
            Boolean status = handleSensitiveScan(data.get("text").toString(), wmNews);
            if(!status){
                return;
            }

            //4. 把文本内容上传到阿里云进行审核- 审核失败 审核文章的状态位2 人工审核更新位3
            status =  handleTextScan(data.get("text").toString(), wmNews);
            if(!status){
                return;
            }

            //5. 把所有的图片下载下载
            //6. 把所有图片的二进制文件上传到阿里云进行图片审核-审核文章的状态位2 人工审核更新位3
            status = handleImageScan((List<String>) data.get("images"), wmNews);
            if(!status){
                return;
            }
            //7. 同步文章数据到文章微服务
            ResponseResult responseResult = saveAppArticle(wmNews);

            if(responseResult.getCode() != 200){
                throw new CustomException(AppHttpCodeEnum.DATA_SYNC_FAIL);
            }

            //8. 更新文章的审核状态位9
            updateWmNews(wmNews, WmNews.Status.PUBLISHED, "审核通过");
        }

    }

    /**
     * 完成自定义敏感词的审核
     * @param text
     * @param wmNews
     * @return
     */
    private Boolean handleSensitiveScan(String text, WmNews wmNews) {
         Boolean flag = Boolean.TRUE;
         //1. 查询所有的铭感词对象
        List<WmSensitive> sensitiveList = sensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));

        //2. 得到所有的敏感词字符串集合
        List<String> list = sensitiveList.stream().map(item -> item.getSensitives()).collect(Collectors.toList());

        //3. 初始化DFA算法的大Map
        SensitiveWordUtil.initMap(list);

        //4. 使用DFA算法完成文本的审核
        Map<String, Integer> map = SensitiveWordUtil.matchWords(text);

        //5. 如果审核失败则更新文章的审核状态为2 同时写入失败原因
        if(null != map && map.size() > 0){
            updateWmNews(wmNews,WmNews.Status.FAIL,"自定义敏感词审核失败了 = " + map);
            flag = Boolean.FALSE;
        }

         return flag;
    }

    private void updateWmNews(WmNews wmNews, WmNews.Status published, String reson) {
        wmNews.setStatus(published.getCode());
        wmNews.setReason(reson);
        newsMapper.updateById(wmNews);
    }

    /**
     * 数据通自媒体端同步到文中微服务
     * @param wmNews
     * @return
     */
    private ResponseResult saveAppArticle(WmNews wmNews) {
        /*ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, dto);

        //单独处理作者相关的数据
        if(wmNews.getUserId() != null){
            WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
            dto.setAuthorId((long)wmNews.getUserId());
            dto.setAuthorName(wmUser.getName());
        }

        //单独处理频道相关的数据
        if(null != wmNews.getChannelId()){
            WmChannel wmChannel = channelMapper.selectById(wmNews.getChannelId());
            dto.setChannelName(wmChannel.getName());
        }

        //其他散列的数据
        dto.setLayout(wmNews.getType());*/

        ArticleDto dto = new ArticleDtoBuilder(wmNews, wmUserMapper, channelMapper)
                .buildBeanCopy()
                .buildAuthor()
                .buildChannel()
                .basicCopy()
                .bulider();
        ResponseResult responseResult = articleClient.saveArticle(dto);

        return responseResult;
    }

    /**
     * 完成图片的审核
     * @param images
     * @param wmNews
     * @return
     */
    private Boolean handleImageScan(List<String> images, WmNews wmNews) throws Exception {

        Boolean flag = Boolean.TRUE;
        if(null == images && images.size() == 0){
            return flag;
        }

        List<byte[]> imageList = new ArrayList<>();

        //1. 对图片进行去重
        images = images.stream().distinct().collect(Collectors.toList());

        //2. 下载每一个图片
        for (String url : images) {
            byte[] bytes = fileStorageService.downLoadFile(url);
            imageList.add(bytes);

            //下载一张图片识别一张图片并审核一张图片的内容
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            BufferedImage image = ImageIO.read(in);
            String str = tess4jClient.doOCR(image);

            System.err.println(str);

            Boolean status = handleSensitiveScan(str, wmNews);
            if(!status){
                flag = Boolean.FALSE;
                return flag;
            }

        }

        Map map = greenImageScan.imageScan(imageList);

        //如果返回值等于block表示审核失败-审核文章的状态位2 并给出审核失败的原因
        if("block".equals(map.get("suggestion"))){
            updateWmNews(wmNews, WmNews.Status.FAIL, "图片存在违规内容,审核失败");

            flag = Boolean.FALSE;
        }else if("review".equals(map.get("suggestion"))){
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, "文中审核失败,图片中包含了不确定的内容需要人工进行审核");

            flag = Boolean.FALSE;
        }

        return flag;
    }

    private Boolean handleTextScan(String text, WmNews wmNews) throws Exception {

        Boolean flag = Boolean.TRUE;

        if(StringUtils.isEmpty(text)){
            return flag;
        }

        Map map = greenTextScan.greeTextScan(text);

        //如果返回值等于block表示审核失败-审核文章的状态位2 并给出审核失败的原因
        if("block".equals(map.get("suggestion"))){
            updateWmNews(wmNews, WmNews.Status.FAIL, "文中审核失败,因为文本中包含了违禁词");

            flag = Boolean.FALSE;
        }else if("review".equals(map.get("suggestion"))){
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, "文中审核失败,里面包含了不确定的文本内容需要人工进行审核");

            flag = Boolean.FALSE;
        }

        return flag;
    }

    /**
     * 抽取文章中所有的内容和图片
     * @param wmNews
     * @return
     */
    private Map<String,Object> handleTextAndImages(WmNews wmNews) {
        StringBuilder sb = new StringBuilder();
        List<String> imageUrls = new ArrayList<>();

        //解析内容中的文本和图片
        if(StringUtils.isNotEmpty(wmNews.getContent())){
            List<Map> list = JSON.parseArray(wmNews.getContent(), Map.class);
            for (Map map : list) {
                if("image".equals(map.get("type"))){
                    imageUrls.add(map.get("value").toString());
                }else{
                    sb.append(map.get("value").toString());
                }
            }
        }

        //处理封面中的图片
        if(StringUtils.isNotEmpty(wmNews.getImages())){
            String[] strs = wmNews.getImages().split(",");
            imageUrls.addAll(Arrays.asList(strs));
        }

        //处理标题中的文本
        sb.append(wmNews.getTitle());

        Map<String,Object> map = new HashMap<>();

        map.put("text", sb.toString());
        map.put("images", imageUrls);

        return map;
    }
}
